/*
 * Copyright � 2011 Arizona State University
 *
 * This file is part of ProMDATE.
 *
 * ProMDATE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ProMDATE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ProMDATE.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ModuleHandler.h>
#include <ControlElement.h>
#include <StringStatus.h>
#include <IntStatus.h>
#include <DoubleStatus.h>
#include <BoolStatus.h>
#include <StringInputControl.h>
#include <IntInputControl.h>
#include <DoubleInputControl.h>
#include <CheckBoxControl.h>
#include <RadioControl.h>
#include <../ByteString.h>
#include <iostream>
#include <time.h>

using namespace std;

ModuleHandler::ModuleHandler(string name) {
	moduleDescription = "";
	moduleName = name;
	pthread_rwlock_init(&moduleLock, NULL);

	numS = 0;
	sList = new StatusElement*[0];
	sOrder = new unsigned int*[0];
	pthread_rwlock_init(&sLock, NULL);

	numC = 0;
	cList = new ControlElement*[0];
	cOrder = new unsigned int*[0];
	pthread_rwlock_init(&cLock, NULL);

	response = "";
	responseFilePath = "";
	pthread_mutex_init(&responseLock, NULL);
	pthread_cond_init(&responseCond, NULL);

	IDs = new unsigned int[0];
	IDsLength = 0;
	pthread_mutex_init(&IDsLock, NULL);

	buttonWaitErr = 0;
	masterLock = new pthread_mutex_t;
	masterCond = new pthread_cond_t;
	pthread_cond_init(masterCond, NULL);
	pthread_mutex_init(masterLock, NULL);
	pthread_mutex_lock(masterLock);

	pthread_rwlock_init(&transferLock, NULL);
	pthread_mutex_init(&getGiveLock, NULL);
	pthread_cond_init(&getGiveCond, NULL);

	conBits = sizeof(int) * 4;
	errsig = -1;
	idOverflow = 0;
	for (int i = 0; i < (sizeof(int) / 2); i++)
		idOverflow |= (0xff << i * 8);

}

ModuleHandler::~ModuleHandler() {
	pthread_rwlock_destroy(&moduleLock);

	for (int i = 0; i < numS; i++) {
		delete sList[i];
		delete[] sOrder[i];
	}
	for (int i = 0; i < numC; i++) {
		delete cList[i];
		delete[] cOrder[i];
	}
	delete[] sList;
	delete[] sOrder;
	delete[] cList;
	delete[] cOrder;
	pthread_rwlock_destroy(&sLock);
	pthread_rwlock_destroy(&cLock);

	pthread_mutex_destroy(&responseLock);
	pthread_cond_destroy(&responseCond);

	pthread_mutex_destroy(&IDsLock);
	delete[] IDs;

	pthread_mutex_destroy(masterLock);
	pthread_cond_destroy(masterCond);
	delete masterLock;
	delete masterCond;

	pthread_rwlock_destroy(&transferLock);
	pthread_mutex_destroy(&getGiveLock);
	pthread_cond_destroy(&getGiveCond);
}

void ModuleHandler::sendStatusElementBS(int orderindex) {
	int index = orderindex;
	int end = orderindex + 1;
	pthread_rwlock_rdlock(&sLock);
	if (orderindex == idOverflow) {
		index = 0;
		end = numS;
	}

	dataConvert data;
	for (; index < end; index++) {
		int type = sOrder[index][1];
		if (type == REMOVED)
			continue;

		ByteString bs;
		data.d = 0;
		data.i = index;
		bs.insertSection(data.c, sizeof(int));
		data.d = 0;
		data.l = sList[sOrder[index][0]]->getDescription().length();
		bs.insertSection(data.c, sizeof(long));
		bs.addSection(sList[sOrder[index][0]]->getDescription().c_str(), data.l);
		data.d = 0;
		data.i = type;
		bs.insertSection(data.c, sizeof(int));

		if (type == STRINGSTATUS) {
			data.d = 0;
			data.l = sList[sOrder[index][0]]->getValueString().length();
			bs.insertSection(data.c, sizeof(long));
			bs.addSection(sList[sOrder[index][0]]->getValueString().c_str(),
					data.l);
		} else {
			int size = 0;
			data.d = 0;
			switch (type) {
			case INTSTATUS:
				data.i = sizeof(int);
				bs.insertSection(data.c, sizeof(int));
				data.d = 0;
				data.l = sList[sOrder[index][0]]->getValueInt();
				size = sizeof(long);
				break;
			case DOUBLESTATUS:
				data.i = sizeof(double);
				bs.insertSection(data.c, sizeof(int));
				data.d = sList[sOrder[index][0]]->getValueDouble();
				size = sizeof(double);
				break;
			case BOOLSTATUS:
				data.i = sizeof(bool);
				bs.insertSection(data.c, sizeof(int));
				data.d = 0;
				data.b = sList[sOrder[index][0]]->getValueBool();
				size = sizeof(bool);
			}
			bs.insertSection(data.c, size);
		}

	}

	pthread_rwlock_unlock(&sLock);
}

void ModuleHandler::sendControlElementBS(int orderindex) {
	int index = orderindex;
	int end = index + 1;
	pthread_rwlock_rdlock(&cLock);
	if (orderindex == idOverflow) {
		index = 0;
		end = numC;
	}

	dataConvert data;
	int size = 0;
	for (; index < end; index++) {
		int type = cOrder[index][1];
		if (type == REMOVED)
			continue;

		ByteString bs;
		data.d = 0;
		data.i = index;
		bs.insertSection(data.c, sizeof(int));

		data.d = 0;
		data.l = cList[cOrder[index][0]]->getDescription().length();
		bs.insertSection(data.c, sizeof(long));
		bs.addSection(cList[cOrder[index][0]]->getDescription().c_str(), data.l);

		data.d = 0;
		data.l = cList[cOrder[index][0]]->getButtonName().length();
		bs.insertSection(data.c, sizeof(long));
		bs.addSection(cList[cOrder[index][0]]->getButtonName().c_str(), data.l);

		int nCons = cList[cOrder[index][0]]->getNumberControls();

		for (int i = 0; i < nCons; i++) {
			bs.clear();
			data.d = 0;
			Control* con = cList[cOrder[index][0]]->getControlFromOrder(i,
					data.i);

			if (data.i == REMOVED) {
				continue;
			}

			bs.insertSection(data.c, sizeof(int));
			data.d = 0;
			data.l = con->getDescription().length();
			bs.insertSection(data.c, sizeof(long));
			bs.addSection(con->getDescription().c_str(), data.l);

			data.d = 0;
			switch (type) {
			case STRINGCONTROL: {
				data.l = con->getValueString().length();
				bs.insertSection(data.c, sizeof(long));
				bs.addSection(con->getValueString().c_str(), data.l);
				break;
			}
			case INTCONTROL: {
				data.l = sizeof(long);
				bs.insertSection(data.c, sizeof(long));
				data.d = 0;
				data.l = con->getValueInt();
				bs.insertSection(data.c, sizeof(long));
				break;
			}
			case DOUBLECONTROL: {
				data.l = sizeof(double);
				bs.insertSection(data.c, sizeof(long));
				data.d = con->getValueDouble();
				bs.insertSection(data.c, sizeof(double));
				break;
			}
			case CHECKBOXCONTROL:
			case RADIOCONTROL: {
				data.l = sizeof(bool);
				bs.insertSection(data.c, sizeof(long));
				data.b = con->getValueBool();
				bs.insertSection(data.c, sizeof(double));
			}
			}
		}
	}
	pthread_rwlock_unlock(&cLock);
}

unsigned int ModuleHandler::addStatusElement(int type, string desc) {
	unsigned int returnNum = errsig;

	if (numS < idOverflow) {
		pthread_rwlock_rdlock(&sLock);
		unsigned int** newSOrder = new unsigned int*[numS + 1];
		StatusElement** newSList = new StatusElement*[numS + 1];
		for (int i = 0; i < numS; i++) {
			newSOrder[i] = sOrder[i];
			newSList[i] = sList[i];
		}

		switch (type) {
		case STRINGSTATUS:
			newSList[numS] = new StringStatus(desc);
			break;
		case INTSTATUS:
			newSList[numS] = new IntStatus(desc);
			break;
		case DOUBLESTATUS:
			newSList[numS] = new DoubleStatus(desc);
			break;
		case BOOLSTATUS:
			newSList[numS] = new BoolStatus(desc);
			break;
		}
		newSOrder[numS] = new unsigned int[2];
		newSOrder[numS][0] = numS;
		newSOrder[numS][1] = newSList[numS]->getType();
		pthread_rwlock_unlock(&sLock);

		pthread_rwlock_wrlock(&sLock);
		returnNum = numS;
		numS++;
		delete[] sOrder;
		delete[] sList;
		sOrder = newSOrder;
		sList = newSList;
		pthread_rwlock_unlock(&sLock);
	}

	return returnNum;
}

unsigned int ModuleHandler::addControlElement(string desc, string button) {
	unsigned int returnNum = errsig;

	if (numC < idOverflow) {
		pthread_rwlock_rdlock(&cLock);
		unsigned int** newCOrder = new unsigned int*[numC + 1];
		ControlElement** newCList = new ControlElement*[numC + 1];
		for (int i = 0; i < numC; i++) {
			newCOrder[i] = cOrder[i];
			newCList[i] = cList[i];
		}
		newCOrder[numC] = new unsigned int[2];
		newCOrder[numC][0] = ((numC + 1) << conBits) & ~idOverflow;
		newCOrder[numC][1] = CONTROLELEMENT;
		newCList[numC] = new ControlElement(desc, button);
		pthread_rwlock_unlock(&cLock);

		pthread_rwlock_wrlock(&cLock);
		returnNum = newCOrder[numC][0];
		numC++;
		delete[] cOrder;
		delete[] cList;
		cOrder = newCOrder;
		cList = newCList;
		pthread_rwlock_unlock(&cLock);
		updateIDsList();
	}

	return returnNum;
}

void ModuleHandler::updateIDsList() {
	pthread_mutex_lock(&IDsLock);
	IDsLength = 0;

	pthread_rwlock_rdlock(&cLock);
	for (int i = 0; i < numC; i++) {
		if (cList[i] != NULL) {
			unsigned int* newIDs = new unsigned int[IDsLength + 1];
			for (int j = 0; j < IDsLength; j++) {
				newIDs[j] = IDs[j];
			}
			newIDs[IDsLength] = ((i + 1) << conBits) & ~idOverflow;
			delete[] IDs;
			IDs = newIDs;
			IDsLength++;
		}
	}
	pthread_rwlock_unlock(&cLock);
	pthread_mutex_unlock(&IDsLock);
}

unsigned int ModuleHandler::addControl(unsigned int ID, int type, string desc) {
	unsigned int returnNum = errsig;
	unsigned int index = ((ID >> conBits) - 1) & idOverflow;

	pthread_rwlock_rdlock(&cLock);
	if (index < numC) {
		switch (type) {
		case STRINGCONTROL:
			returnNum = cList[index]->addControl(new StringInputControl(desc));
			break;
		case INTCONTROL:
			returnNum = cList[index]->addControl(new IntInputControl(desc));
			break;
		case DOUBLECONTROL:
			returnNum = cList[index]->addControl(new DoubleInputControl(desc));
			break;
		case CHECKBOXCONTROL:
			returnNum = cList[index]->addControl(new CheckBoxControl(desc));
			break;
		case RADIOCONTROL:
			returnNum = cList[index]->addControl(new RadioControl(desc));
			break;
		}
	}
	pthread_rwlock_unlock(&cLock);

	return returnNum;
}

unsigned int ModuleHandler::setOrder(int type, unsigned int* ids, int* order,
		int length) {
	int returnNum = -1;
	if (length > 0) {
		if (type == STATUSELEMENT) {
			pthread_rwlock_wrlock(&sLock);
			for (int i = 0; i < numS; i++)
				sOrder[i][0] = -1;
			for (int i = 0; i < length; i++) {
				if (ids[i] < numS && order[i] < numS) {
					sOrder[order[i]][0] = ids[i];
					sOrder[order[i]][1] = sList[ids[i]]->getType();
				}
			}

			for (int i = 0; i < numS; i++) {
				if (sOrder[i][0] == -1) {
					bool jNotUsed = true;
					for (int j = 0; j < numS; j++) {
						for (int k = 0; k < numS; k++) {
							if (sOrder[k][0] == j) {
								jNotUsed = false;
								break;
							}
						}
						if (jNotUsed == true) {
							sOrder[i][0] = j;
							sOrder[i][1] = sList[j]->getType();
							break;
						}
					}
				}
			}
			pthread_rwlock_unlock(&sLock);
			returnNum = 0;
		} else if (type == CONTROLELEMENT) {
			pthread_rwlock_wrlock(&cLock);
			for (int i = 0; i < numC; i++)
				cOrder[i][0] = -1;
			for (int i = 0; i < length; i++) {
				unsigned int index = ((ids[i] >> conBits) - 1) & idOverflow;
				if (index < numC && order[i] < numC) {
					cOrder[order[i]][0] = ids[i];
					cOrder[order[i]][1] = cList[index]->getType();
				}
			}

			for (int i = 0; i < numC; i++) {
				if (cOrder[i][0] == -1) {
					bool jNotUsed = true;
					for (int j = 0; j < numC; j++) {
						for (int k = 0; k < numC; k++) {
							if (cOrder[k][0] == j) {
								jNotUsed = false;
								break;
							}
						}
						if (jNotUsed == true) {
							cOrder[i][0] = ((j + 1) << conBits) & ~idOverflow;
							cOrder[i][1] = cList[j]->getType();
							break;
						}
					}
				}
			}
			pthread_rwlock_unlock(&cLock);
			returnNum = 0;
		} else if (type == CONTROL) {
			unsigned int conElIndex = ((ids[0] >> conBits) - 1) & idOverflow;
			if (conElIndex < numC) {
				returnNum = 0;
				int* conIDs = new int[length];
				for (int i = 0; i < length; i++) {
					if (conElIndex != ((ids[i] >> conBits) - 1) & idOverflow) {
						returnNum = -1;
						break;
					}
					conIDs[i] = ids[i] & idOverflow;
				}

				if (returnNum == 0) {
					pthread_rwlock_rdlock(&cLock);
					cList[conElIndex]->setOrder(conIDs, order, length);
					pthread_rwlock_unlock(&cLock);
					delete[] conIDs;
				}
			}
		}
	}

	return returnNum;
}

unsigned int ModuleHandler::remove(unsigned int id) {
	int returnNum = -1;
	if (id < idOverflow) {
		pthread_rwlock_wrlock(&sLock);
		if (sList[id] != NULL) {
			delete sList[id];
			sList[id] = NULL;
			for (int i = 0; i < numS; i++) {
				if (id == sOrder[i][0]) {
					sOrder[i][1] = REMOVED;
					break;
				}
			}
		}
		pthread_rwlock_unlock(&sLock);
		returnNum = 0;
	} else if (id & idOverflow == 0) {
		unsigned int index = ((id >> conBits) - 1) & idOverflow;
		pthread_rwlock_wrlock(&cLock);
		if (cList[index] != NULL) {
			delete cList[index];
			cList[index] = NULL;
			for (int i = 0; i < numC; i++) {
				cOrder[i][1] = REMOVED;
				break;
			}
		}
		pthread_rwlock_unlock(&cLock);
		updateIDsList();
		returnNum = 0;
	} else if (id != -1) {
		unsigned int conElIndex = ((id >> conBits) - 1) & idOverflow;
		unsigned int conID = id & idOverflow;
		pthread_rwlock_wrlock(&cLock);
		if (conElIndex < numC) {
			if (cList[conElIndex] != NULL)
				returnNum = cList[conElIndex]->removeControl(conID);
			else
				returnNum = 0;
		}
		pthread_rwlock_unlock(&cLock);
	}

	return returnNum;
}

void ModuleHandler::setControlElementButtonName(unsigned int ID,
		string newButtonName) {
	unsigned int conElIndex = ((ID >> conBits) - 1) & idOverflow;

	pthread_rwlock_rdlock(&cLock);
	if (conElIndex < numC && cList[conElIndex] != NULL)
		cList[conElIndex]->setButtonName(newButtonName);
	pthread_rwlock_unlock(&cLock);
}

string ModuleHandler::getControlElementButtonName(unsigned int ID) {
	unsigned int conElIndex = ((ID >> conBits) - 1) & idOverflow;

	string returnString = "";
	pthread_rwlock_rdlock(&cLock);
	if (conElIndex < numC && cList[conElIndex] != NULL)
		returnString += cList[conElIndex]->getButtonName();
	pthread_rwlock_unlock(&cLock);

	return returnString;
}

int ModuleHandler::getNumberControlsInControlElement(unsigned int ID) {
	unsigned int conElIndex = ((ID >> conBits) - 1) & idOverflow;

	unsigned int returnInt = -1;
	pthread_rwlock_rdlock(&cLock);
	if (conElIndex < numC && cList[conElIndex] != NULL)
		returnInt = cList[conElIndex]->getNumberControls();
	pthread_rwlock_unlock(&cLock);

	return returnInt;
}

string ModuleHandler::getDescription(unsigned int id) {
	string returnString = "";
	if (id < idOverflow) {
		pthread_rwlock_rdlock(&sLock);
		if (id < numS && sList[id] != NULL)
			returnString = sList[id]->getDescription();
		pthread_rwlock_unlock(&sLock);
	} else if (id & idOverflow == 0) {
		unsigned int index = ((id >> conBits) - 1) & idOverflow;

		pthread_rwlock_rdlock(&cLock);
		if (index < numC && cList[index] != NULL)
			returnString = cList[id]->getDescription();
		pthread_rwlock_unlock(&cLock);
	} else if (id != -1) {
		unsigned int conElIndex = ((id >> conBits) - 1) & idOverflow;
		unsigned int conID = id & idOverflow;

		pthread_rwlock_rdlock(&cLock);
		if (conElIndex < numC && cList[conElIndex] != NULL) {
			Control* cTemp = cList[conElIndex]->getControl(conID);
			if (cTemp != NULL)
				returnString = cTemp->getDescription();
		}
		pthread_rwlock_unlock(&cLock);
	}

	return returnString;
}

void ModuleHandler::setValue(unsigned int id, void* newValue) {
	if (id < idOverflow) {
		pthread_rwlock_rdlock(&sLock);
		if (id < numS && sList[id] != NULL)
			sList[id]->setValue(newValue);
		pthread_rwlock_unlock(&sLock);
	} else if (id != -1 && (id & idOverflow) != 0) {
		unsigned int conElIndex = ((id >> conBits) - 1) & idOverflow;
		unsigned int conID = id & idOverflow;

		pthread_rwlock_rdlock(&cLock);
		if (conElIndex < numC && cList[conElIndex] != NULL) {
			Control* cTemp = cList[conElIndex]->getControl(conID);
			if (cTemp != NULL)
				cTemp->setValue(newValue);
		}
		pthread_rwlock_unlock(&cLock);
	}
}

string ModuleHandler::getValueString(unsigned int id) {
	string returnString = "";

	if (id < idOverflow) {
		pthread_rwlock_rdlock(&sLock);
		if (id < numS && sList[id] != NULL)
			returnString = sList[id]->getValueString();
		pthread_rwlock_unlock(&sLock);
	} else if (id != -1 && (id & idOverflow) != 0) {
		unsigned int conElIndex = ((id >> conBits) - 1) & idOverflow;
		unsigned int conID = id & idOverflow;

		pthread_rwlock_rdlock(&cLock);
		if (conElIndex < numC && cList[conElIndex] != NULL) {
			Control* cTemp = cList[conElIndex]->getControl(conID);
			if (cTemp != NULL)
				returnString = cTemp->getValueString();
		}
		pthread_rwlock_unlock(&cLock);
	}

	return returnString;
}

long ModuleHandler::getValueInt(unsigned int id) {
	long returnLong = -1;

	if (id < idOverflow) {
		pthread_rwlock_rdlock(&sLock);
		if (id < numS && sList[id] != NULL)
			returnLong = sList[id]->getValueInt();
		pthread_rwlock_unlock(&sLock);
	} else if (id != -1 && (id & idOverflow) != 0) {
		unsigned int conElIndex = ((id >> conBits) - 1) & idOverflow;
		unsigned int conID = id & idOverflow;

		pthread_rwlock_rdlock(&cLock);
		if (conElIndex < numC && cList[conElIndex] != NULL) {
			Control* cTemp = cList[conElIndex]->getControl(conID);
			if (cTemp != NULL)
				returnLong = cTemp->getValueInt();
		}
		pthread_rwlock_unlock(&cLock);
	}

	return returnLong;
}

double ModuleHandler::getValueDouble(unsigned int id) {
	double returnDouble = -1;

	if (id < idOverflow) {
		pthread_rwlock_rdlock(&sLock);
		if (id < numS && sList[id] != NULL)
			returnDouble = sList[id]->getValueDouble();
		pthread_rwlock_unlock(&sLock);
	} else if (id != -1 && (id & idOverflow) != 0) {
		unsigned int conElIndex = ((id >> conBits) - 1) & idOverflow;
		unsigned int conID = id & idOverflow;

		pthread_rwlock_rdlock(&cLock);
		if (conElIndex < numC && cList[conElIndex] != NULL) {
			Control* cTemp = cList[conElIndex]->getControl(conID);
			if (cTemp != NULL)
				returnDouble = cTemp->getValueDouble();
		}
		pthread_rwlock_unlock(&cLock);
	}

	return returnDouble;
}

bool ModuleHandler::getValueBool(unsigned int id) {
	bool returnBool = false;

	if (id < idOverflow) {
		pthread_rwlock_rdlock(&sLock);
		if (id < numS && sList[id] != NULL)
			returnBool = sList[id]->getValueBool();
		pthread_rwlock_unlock(&sLock);
	} else if (id != -1 && (id & idOverflow) != 0) {
		unsigned int conElIndex = ((id >> conBits) - 1) & idOverflow;
		unsigned int conID = id & idOverflow;

		pthread_rwlock_rdlock(&cLock);
		if (conElIndex < numC && cList[conElIndex] != NULL) {
			Control* cTemp = cList[conElIndex]->getControl(conID);
			if (cTemp != NULL)
				returnBool = cTemp->getValueBool();
		}
		pthread_rwlock_unlock(&cLock);
	}

	return returnBool;
}

unsigned int ModuleHandler::waitForButtonPush() {
	buttonWaitErr = 0;
	pthread_mutex_lock(&IDsLock);
	if (IDsLength == 0) {
		pthread_rwlock_wrlock(&moduleLock);
		buttonWaitErr = NOCONELS;
		pthread_rwlock_unlock(&moduleLock);
		return -1;
	}

	blockingStruct* sList = new blockingStruct[IDsLength];
	pthread_t* tList = new pthread_t[IDsLength];
	unsigned int returnID = -1;
	bool masterBool = false;

	for (int i = 0; i < IDsLength; i++) {
		sList[i].ID = IDs[i];
		sList[i].returnID = &returnID;
		sList[i].masterFlag = &masterBool;
		sList[i].masterCond = masterCond;
		sList[i].masterLock = masterLock;
		pthread_rwlock_rdlock(&cLock);
		sList[i].cond = cList[IDs[i]]->getButtonCond();
		sList[i].lock = cList[IDs[i]]->getButtonLock();
		pthread_mutex_lock(sList[i].lock);
		pthread_rwlock_unlock(&cLock);

		pthread_create(&tList[i], NULL, &buttonBroadcastListener,
				static_cast<void*> (&sList[i]));
	}

	for (int i = 0; i < IDsLength; i++) {
		pthread_cond_wait(sList[i].cond, sList[i].lock);
		pthread_mutex_unlock(sList[i].lock);
	}

	pthread_cond_wait(masterCond, masterLock);
	masterBool = true;

	for (int i = 0; i < IDsLength; i++) {
		if (returnID != IDs[i]) {
			pthread_mutex_lock(sList[i].lock);
			pthread_cond_signal(sList[i].cond);
			pthread_mutex_unlock(sList[i].lock);
		}

		pthread_join(tList[i], NULL);
	}

	pthread_mutex_unlock(&IDsLock);

	delete[] sList;
	delete[] tList;

	if (returnID == -1) {
		pthread_rwlock_wrlock(&moduleLock);
		buttonWaitErr = ABANDONED;
		pthread_rwlock_unlock(&moduleLock);
	}

	return returnID;
}

void* ModuleHandler::buttonBroadcastListener(void* structVoid) {
	blockingStruct* s = static_cast<blockingStruct*> (structVoid);

	pthread_mutex_lock(s->lock);
	pthread_cond_signal(s->cond);
	pthread_cond_wait(s->cond, s->lock);
	pthread_mutex_unlock(s->lock);

	if (!(*s->masterFlag)) {
		pthread_mutex_lock(s->masterLock);
		*s->returnID = s->ID;
		pthread_cond_signal(s->masterCond);
		pthread_mutex_unlock(s->masterLock);
	}

	pthread_exit(NULL);
	return NULL;
}

void ModuleHandler::abandonButtonWait() {
	pthread_mutex_lock(masterLock);
	pthread_cond_signal(masterCond);
	pthread_mutex_unlock(masterLock);
}

string ModuleHandler::buttonPushed(unsigned int ID, string& path) {
	unsigned int index = ((ID >> conBits) - 1) & idOverflow;
	string response = "";
	pthread_rwlock_rdlock(&cLock);
	if (index < numC && cList[index] != NULL) {
		pthread_mutex_lock(&responseLock);

		pthread_rwlock_wrlock(&transferLock);
		pthread_mutex_lock(masterLock);
		pthread_mutex_lock(cList[index]->getButtonLock());
		pthread_cond_signal(cList[index]->getButtonCond());
		pthread_mutex_unlock(cList[index]->getButtonLock());
		pthread_mutex_unlock(masterLock);
		pthread_rwlock_unlock(&transferLock);
		pthread_rwlock_unlock(&cLock);

		pthread_cond_wait(&responseCond, &responseLock);
		path = responseFilePath;
		response = this->response;
		pthread_mutex_unlock(&responseLock);
	} else
		pthread_rwlock_unlock(&cLock);

	return response;
}

void ModuleHandler::giveResponse(string s, string path) {
	pthread_mutex_lock(&responseLock);
	response = s;
	responseFilePath = path;
	pthread_cond_signal(&responseCond);
	pthread_mutex_unlock(&responseLock);
}

string ModuleHandler::getModuleName() {
	pthread_rwlock_rdlock(&moduleLock);
	string moduleNameSafe = moduleName;
	pthread_rwlock_unlock(&moduleLock);
	return moduleNameSafe;
}

string ModuleHandler::getModuleDescription() {
	pthread_rwlock_rdlock(&moduleLock);
	string moduleDescriptionSafe = moduleDescription;
	pthread_rwlock_unlock(&moduleLock);
	return moduleDescriptionSafe;
}

void ModuleHandler::setModuleDescription(string desc) {
	pthread_rwlock_wrlock(&moduleLock);
	moduleDescription = desc;
	pthread_rwlock_unlock(&moduleLock);
}

void ModuleHandler::giveLock() {
	pthread_rwlock_rdlock(&transferLock);
	pthread_mutex_lock(&getGiveLock);
	pthread_mutex_unlock(masterLock);
	pthread_cond_wait(&getGiveCond, &getGiveLock);
	pthread_mutex_unlock(&getGiveLock);
	pthread_rwlock_unlock(&transferLock);
}

void ModuleHandler::getLock() {
	pthread_rwlock_rdlock(&transferLock);
	pthread_mutex_lock(masterLock);
	pthread_mutex_lock(&getGiveLock);
	pthread_cond_signal(&getGiveCond);
	pthread_mutex_unlock(&getGiveLock);
	pthread_rwlock_unlock(&transferLock);
}
